import { createContext, FC, useContext, useRef } from 'react';
import { BrowserRouter, matchPath, useLocation, useRoutes } from 'react-router-dom';

import routes from '@/routes';

// 设置公共组件类型
interface KeepAliveLayoutProps {
  keepalive: (string | RegExp)[]; // 需要缓存的路由
  keepElements?: any; // 缓存路由对应元素
  children?: JSX.Element;
  dropByCacheKey?: (path: string) => void;
}

// 在组件外部建立一个Context
export const KeepAliveContext = createContext<KeepAliveLayoutProps>({
  keepalive: [],
  keepElements: {},
});

// 基于页面缓存设置条件判断
const isKeepPath = (aliveList: (string | RegExp)[], path: string) => {
  let isKeep = false;
  aliveList.map(item => {
    if (item === path) {
      isKeep = true;
    }
    if (item instanceof RegExp && item.test(path)) {
      isKeep = true;
    }
  });
  return isKeep;
};

// 判断当前页面是否已缓存，是则控制hidden开关显示 ，不是则正常渲染
export const useKeepRoutes = () => {
  const location = useLocation();
  const element = useRoutes(routes);
  const { keepElements, keepalive } = useContext(KeepAliveContext);
  const isKeep = isKeepPath(keepalive, location.pathname);
  // 保存缓存路由的jsx
  if (isKeep) {
    keepElements.current[location.pathname] = element;
  }
  return (
    <>
      {Object.entries(keepElements.current).map(([pathname, element]: any) => (
        <div
          key={pathname}
          style={{ height: '100%', width: '100%', position: 'relative', overflow: 'hidden auto' }}
          hidden={!matchPath(location.pathname, pathname)}
        >
          {element}
        </div>
      ))}
      <div
        hidden={isKeep}
        style={{ height: '100%', width: '100%', position: 'relative', overflow: 'hidden auto' }}
      >
        {!isKeep && element}
      </div>
    </>
  );
};

// 封装公共组件
const CacheRouter: FC<KeepAliveLayoutProps> = props => {
  const { keepalive, ...other } = props;
  const keepElements = useRef<any>({});
  function dropByCacheKey(path: string) {
    keepElements.current[path] = null;
  }
  return (
    <BrowserRouter>
      <KeepAliveContext.Provider value={{ keepalive, keepElements, dropByCacheKey }} {...other} />
    </BrowserRouter>
  );
};

export default CacheRouter;
